package meiqia.flutter_meiqia

import android.Manifest
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.os.Build
import android.support.v4.app.ActivityCompat
import android.text.TextUtils
import com.meiqia.core.MQManager
import com.meiqia.core.MQManager.getMeiqiaSDKVersion
import com.meiqia.core.MQMessageManager
import com.meiqia.core.MQScheduleRule
import com.meiqia.core.bean.MQMessage
import com.meiqia.core.callback.*
import com.meiqia.meiqiasdk.activity.MQMessageFormActivity
import com.meiqia.meiqiasdk.util.MQConfig
import com.meiqia.meiqiasdk.util.MQIntentBuilder
import com.meiqia.meiqiasdk.util.MQUtils
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
import java.io.File

class FlutterMeiqiaPlugin(registrar: Registrar) : MethodCallHandler, EventChannel.StreamHandler {

    private val mRegistrar = registrar
    private var mOfflineMessageReceiver: BroadcastReceiver? = null

    companion object {
        private const val TAG = "MEI QIA"
        private const val FLUTTER_CHANNEL = "flutter_meiqia"
        private const val FLUTTER_EVENT = "flutter_meiqia_event"
        private const val CODE_SUCCEED = 1
        private const val CODE_FAILED = 2
        private const val MESSAGE_SUCCEED = "succeed"
        private const val MESSAGE_FAILED = "failed"
        private const val REQUEST_PERMISSION_CODE = 1024
        private const val OTHER_ACTION = 0xff00
        private const val MESSAGE_RECEIVER = 0xff01
        private const val AGENT_INPUTTING = 0xff02
        private const val AGENT_CHANGE = 0xff03
        private const val STR_MESSAGE_RECEIVER = "Event[message receiver]"
        private const val STR_AGENT_INPUTTING = "Event[agent inputting]"
        private const val STR_AGENT_CHANGE = "Event[agent change]"
        private const val STR_OTHER_ACTION = "Event[other action]"
        private var hasRegistered = false

        @JvmStatic
        fun registerWith(registrar: Registrar) {
            val channel = MethodChannel(registrar.messenger(), FLUTTER_CHANNEL)
            val stream = EventChannel(registrar.messenger(), FLUTTER_EVENT)
            val plugin = FlutterMeiqiaPlugin(registrar)
            channel.setMethodCallHandler(plugin)
            stream.setStreamHandler(plugin)
        }
    }

    @SuppressLint("PrivateResource")
    override fun onMethodCall(call: MethodCall, result: Result) {
        if (TextUtils.equals(call.method, "registerMeiQia")) {
            registerMeiQia(call, result)
        } else {
            if (!hasRegistered) {
                result.error("IllegalStateException", "not ready",
                        "MeiQia is not initialed, and call registerMeiQia first")
                return
            }

            mRegistrar.addRequestPermissionsResultListener { code, _, grantedResult ->
                if (code == REQUEST_PERMISSION_CODE &&
                        grantedResult.isNotEmpty() && grantedResult[0] == PackageManager.PERMISSION_GRANTED) {
                    gotoConversion(call)
                    true
                } else {
                    MQUtils.show(mRegistrar.context(), com.meiqia.meiqiasdk.R.string.mq_sdcard_no_permission)
                    false
                }
            }

            when (call.method) {
                "openMeiQiaService" -> openMeiQiaService()
                "closeMeiQiaService" -> closeMeiQiaService()
                "conversation" -> conversation(call)
                "leaveMessage" -> leaveMessage()
                "getNewMeiQiaId" -> getNewMeiQiaId(result)
                "setClientOffline" -> setClientOffline(result)
                "getMQMessage" -> getMQMessage(call, result)
                "registerDeviceToken" -> registerDeviceToken(call, result)
                "getCurrentAgent" -> getCurrentAgent(result)
                "getCurrentClientId" -> getCurrentClientId(result)
                "createMQClient" -> createMQClient(result)
                "updateMessage" -> updateMessage(call)
                "endCurrentConversation" -> endCurrentConversation(result)
                "setCurrentClient" -> setCurrentClient(call, result)
                "getUnreadMessages" -> getUnreadMessages(call, result)
                "getSdkVersion" -> getSdkVersion(result)
                else -> result.notImplemented()
            }
        }
    }

    override fun onListen(argument: Any?, sink: EventChannel.EventSink?) {
        mOfflineMessageReceiver = createEventListener(sink)
        mRegistrar.context().registerReceiver(
                mOfflineMessageReceiver,
                IntentFilter().apply {
                    addAction(MQMessageManager.ACTION_NEW_MESSAGE_RECEIVED)
                    addAction(MQMessageManager.ACTION_AGENT_INPUTTING)
                    addAction(MQMessageManager.ACTION_AGENT_CHANGE_EVENT)
                }
        )
    }

    override fun onCancel(argument: Any?) {
        if (mOfflineMessageReceiver != null) {
            mRegistrar.context().unregisterReceiver(mOfflineMessageReceiver)
            mOfflineMessageReceiver = null
        }
    }

    private fun createEventListener(sink: EventChannel.EventSink?)
            : BroadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            if (intent == null || intent.action.isNullOrEmpty()) return

            val mqMessageManager = MQMessageManager.getInstance(context)
            val resp = HashMap<String, Any>()
            val data = HashMap<String, Any?>()

            when (intent.action) {
                MQMessageManager.ACTION_NEW_MESSAGE_RECEIVED -> {
                    val msgId = intent.getStringExtra("msgId")
                    val message = mqMessageManager.getMQMessage(msgId)

                    // message info
                    data["agentId"] = message.agent_id
                    data["agentNickname"] = message.agent_nickname
                    data["avatar"] = message.avatar
                    data["content"] = message.content
                    data["contentRobot"] = message.content_robot
                    data["contentType"] = message.content_type
                    data["conversationId"] = message.conversation_id
                    data["createdOn"] = message.created_on
                    data["enterpriseId"] = message.enterprise_id
                    data["extra"] = message.extra
                    data["fromType"] = message.from_type
                    data["id"] = message.id
                    data["isAlreadyFeedback"] = message.isAlreadyFeedback
                    data["isRobot"] = message.isRobot
                    data["isRead"] = message.is_read
                    data["mediaUrl"] = message.media_url
                    data["questionId"] = message.question_id
                    data["status"] = message.status
                    data["subType"] = message.sub_type
                    data["trackId"] = message.track_id
                    data["type"] = message.type

                    resp["code"] = MESSAGE_RECEIVER
                    resp["message"] = STR_MESSAGE_RECEIVER
                }

                MQMessageManager.ACTION_AGENT_INPUTTING -> {
                    resp["code"] = AGENT_INPUTTING
                    resp["message"] = STR_AGENT_INPUTTING
                }

                MQMessageManager.ACTION_AGENT_CHANGE_EVENT -> {
                    val agent = mqMessageManager.currentAgent

                    // agent info
                    data["avatar"] = agent.avatar
                    data["cellphone"] = agent.cellphone
                    data["email"] = agent.email
                    data["enterpriseId"] = agent.enterprise_id
                    data["id"] = agent.id
                    data["agentId"] = agent.agentId
                    data["nickname"] = agent.nickname
                    data["publicCellphone"] = agent.public_cellphone
                    data["publicEmail"] = agent.public_email
                    data["qq"] = agent.qq
                    data["realname"] = agent.realname
                    data["telephone"] = agent.telephone
                    data["weixin"] = agent.weixin
                    data["isOnline"] = agent.isOnLine
                    data["privilege"] = agent.privilege

                    resp["code"] = AGENT_CHANGE
                    resp["message"] = STR_AGENT_CHANGE
                }

                else -> {
                    resp["code"] = OTHER_ACTION
                    resp["message"] = STR_OTHER_ACTION
                }
            }

            resp["data"] = data
            sink?.success(resp)
        }
    }

    private fun registerMeiQia(call: MethodCall, result: Result) {
        val key = call.argument<String>("key")!!
        val debugModel = call.argument<Boolean>("debug")!!
        val gravity = call.argument<Int>("gravity")

        MQManager.setDebugMode(debugModel)

        val resp = HashMap<String, Any>()
        val data = HashMap<String, Any?>()

        MQConfig.init(mRegistrar.context(), key, object : OnInitCallback {
            override fun onSuccess(clientId: String?) {
                data["clientId"] = clientId
                succeedCallback(resp, data, result)

                hasRegistered = true

                MQConfig.ui.titleGravity = if (gravity == 1)
                    MQConfig.ui.MQTitleGravity.LEFT else MQConfig.ui.MQTitleGravity.CENTER
            }

            override fun onFailure(code: Int, message: String?) {
                failedCallback(resp, code, message, result)
            }
        })
    }

    private fun openMeiQiaService() =
            MQManager.getInstance(mRegistrar.context()).openMeiqiaService()

    private fun closeMeiQiaService() =
            MQManager.getInstance(mRegistrar.context()).closeMeiqiaService()

    private fun conversation(call: MethodCall) =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
                    mRegistrar.activity().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(mRegistrar.activity(),
                        arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                        REQUEST_PERMISSION_CODE)
            } else {
                gotoConversion(call)
            }

    @Suppress("UNCHECKED_CAST")
    private fun gotoConversion(call: MethodCall) {
        val argument = call.arguments as HashMap<String, Any>

        if (argument.isEmpty()) {
            mRegistrar.context().startActivity(MQIntentBuilder(mRegistrar.context())
                    .build()
                    .apply {
                        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    })
        } else {
            val clientId = call.argument<String>("clientId")
            val customisedId = call.argument<String>("customisedId")
            val agentToken = call.argument<String>("agentToken")
            val groupToken = call.argument<String>("groupToken")

            val client = call.argument<HashMap<String, Any?>>("clientParams")
            val clientInfo = hashMapOf<String, String>()

            if (client != null) {
                if (client.containsKey("extras")) {
                    val extras = client["extras"] as HashMap<String, String>

                    for (key in extras.keys) {
                        val value = extras[key].toString()
                        if (value.isNotEmpty()) {
                            clientInfo[key] = value
                        }
                    }
                    client.remove("extras")
                }

                for (key in client.keys) {
                    val value = client[key].toString()
                    if (value.isNotEmpty()) {
                        clientInfo[key] = value
                    }
                }
            }

            var preSendMessage: String? = null
            var preSendImage: String? = null
            val preSend = call.argument<HashMap<String, String>>("preSend")

            if (preSend != null) {
                preSendMessage = preSend["message"]
                preSendImage = preSend["image"]
            }

            mRegistrar.context().startActivity(
                    MQIntentBuilder(mRegistrar.context())
                            .apply {
                                if (!clientId.isNullOrEmpty()) setClientId(clientId)
                                if (!customisedId.isNullOrEmpty()) setCustomizedId(customisedId)
                                if (!agentToken.isNullOrEmpty()) setScheduledAgent(agentToken)
                                if (!groupToken.isNullOrEmpty()) setScheduledGroup(groupToken)
                                setScheduleRule(MQScheduleRule.REDIRECT_ENTERPRISE)

                                if (clientInfo.isNotEmpty()) setClientInfo(clientInfo)

                                if (!preSendMessage.isNullOrEmpty()) setPreSendTextMessage(preSendMessage)
                                if (!preSendImage.isNullOrEmpty()) setPreSendImageMessage(File(preSendImage))
                            }
                            .build()
                            .apply {
                                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                            }
            )
        }
    }

    private fun leaveMessage() =
            mRegistrar.context().startActivity(Intent(mRegistrar.context(),
                    MQMessageFormActivity::class.java).apply {
                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            })

    private fun getNewMeiQiaId(result: Result) =
            MQManager.getInstance(mRegistrar.context())
                    .createMQClient(object : OnGetMQClientIdCallBackOn {
                        override fun onSuccess(mqClientId: String?) {
                            result.success(mqClientId)
                        }

                        override fun onFailure(code: Int, message: String?) {
                            result.error("$code", message, "")
                        }
                    })

    private fun setClientOffline(result: Result) {
        MQManager.getInstance(mRegistrar.context()).setClientOffline()
        result.success(true)
    }

    private fun getMQMessage(call: MethodCall, result: Result) {
        val timeStamp = call.argument<Long>("timeStamp")!!
        val length = call.argument<Int>("length")!!
        val fromService = call.argument<Boolean>("fromService")!!

        val resp = HashMap<String, Any>()
        val data = HashMap<String, Any?>()

        if (fromService) {
            MQManager.getInstance(mRegistrar.context())
                    .getMQMessageFromService(timeStamp, length, object : OnGetMessageListCallback {
                        override fun onSuccess(messages: MutableList<MQMessage>?) {
                            data["messages"] = messages
                            succeedCallback(resp, data, result)
                        }

                        override fun onFailure(code: Int, message: String?) {
                            failedCallback(resp, code, message, result)
                        }
                    })
        } else {
            MQManager.getInstance(mRegistrar.context())
                    .getMQMessageFromDatabase(timeStamp, length, object : OnGetMessageListCallback {
                        override fun onSuccess(messages: MutableList<MQMessage>?) {
                            data["messages"] = messages
                            succeedCallback(resp, data, result)
                        }

                        override fun onFailure(code: Int, message: String?) {
                            failedCallback(resp, code, message, result)
                        }
                    })
        }
    }

    private fun registerDeviceToken(call: MethodCall, result: Result) {
        val token = call.argument<String>("token")
        val resp = HashMap<String, Any>()
        MQManager.getInstance(mRegistrar.context()).registerDeviceToken(token, object : OnRegisterDeviceTokenCallback {
            override fun onSuccess() {
                succeedCallback(resp, HashMap(), result)
            }

            override fun onFailure(code: Int, message: String?) {
                failedCallback(resp, code, message, result)
            }
        })
    }

    private fun getCurrentAgent(result: Result) {
        val resp = HashMap<String, Any>()
        val data = HashMap<String, Any?>()

        val agent = MQManager.getInstance(mRegistrar.context()).currentAgent
        if (agent == null) {
            resp["code"] = CODE_FAILED
            resp["message"] = MESSAGE_FAILED
        } else {
            resp["code"] = CODE_SUCCEED
            resp["message"] = MESSAGE_SUCCEED

            // agent info
            data["avatar"] = agent.avatar
            data["cellphone"] = agent.cellphone
            data["email"] = agent.email
            data["enterpriseId"] = agent.enterprise_id
            data["id"] = agent.id
            data["agentId"] = agent.agentId
            data["nickname"] = agent.nickname
            data["publicCellphone"] = agent.public_cellphone
            data["publicEmail"] = agent.public_email
            data["qq"] = agent.qq
            data["realname"] = agent.realname
            data["telephone"] = agent.telephone
            data["weixin"] = agent.weixin
            data["isOnline"] = agent.isOnLine
            data["privilege"] = agent.privilege
        }

        resp["data"] = data
        result.success(resp)
    }

    private fun getCurrentClientId(result: Result) =
            result.success(MQManager.getInstance(mRegistrar.context()).currentClientId)

    private fun createMQClient(result: Result) {
        val resp = HashMap<String, Any>()
        MQManager.getInstance(mRegistrar.context()).createMQClient(
                object : OnGetMQClientIdCallBackOn {
                    override fun onSuccess(clientId: String?) {
                        succeedCallback(resp, hashMapOf(Pair("client", clientId)), result)
                    }

                    override fun onFailure(code: Int, message: String?) {
                        failedCallback(resp, code, message, result)
                    }
                })
    }

    private fun updateMessage(call: MethodCall) {
        val messageId = call.argument<Long>("messageId")!!
        val isRead = call.argument<Boolean>("isRead")!!
        MQManager.getInstance(mRegistrar.context())
                .updateMessage(messageId, isRead)
    }

    private fun endCurrentConversation(result: Result) {
        val resp = HashMap<String, Any>()
        MQManager.getInstance(mRegistrar.context())
                .endCurrentConversation(object : OnEndConversationCallback {
                    override fun onSuccess() {
                        succeedCallback(resp, HashMap(), result)
                    }

                    override fun onFailure(code: Int, message: String?) {
                        failedCallback(resp, code, message, result)
                    }
                })
    }

    private fun setCurrentClient(call: MethodCall, result: Result) {
        val clientIdOrCustomizedId = call.argument<String>("clientOrCustomizedId")
        val resp = HashMap<String, Any>()

        MQManager.getInstance(mRegistrar.context()).setCurrentClient(clientIdOrCustomizedId,
                object : SimpleCallback {
                    override fun onSuccess() {
                        succeedCallback(resp, HashMap(), result)
                    }

                    override fun onFailure(code: Int, message: String?) {
                        failedCallback(resp, code, message, result)
                    }
                })
    }

    private fun getUnreadMessages(call: MethodCall, result: Result) {
        val clientId = call.argument<String?>("clientOrCustomizedId")

        val resp = HashMap<String, Any>()
        val data = HashMap<String, Any?>()

        if (clientId.isNullOrEmpty()) {
            MQManager.getInstance(mRegistrar.context())
                    .getUnreadMessages(object : OnGetMessageListCallback {
                        override fun onSuccess(messages: MutableList<MQMessage>?) {
                            data["messages"] = messages
                            succeedCallback(resp, data, result)
                        }

                        override fun onFailure(code: Int, message: String?) {
                            failedCallback(resp, code, message, result)
                        }
                    })
        } else {
            MQManager.getInstance(mRegistrar.context())
                    .getUnreadMessages(clientId, object : OnGetMessageListCallback {
                        override fun onSuccess(messages: MutableList<MQMessage>?) {
                            data["messages"] = messages
                            succeedCallback(resp, data, result)
                        }

                        override fun onFailure(code: Int, message: String?) {
                            failedCallback(resp, code, message, result)
                        }
                    })
        }
    }

    private fun getSdkVersion(result: Result) {
        result.success(getMeiqiaSDKVersion())
    }

    /** 成功回调 */
    private fun succeedCallback(resp: HashMap<String, Any>, data: HashMap<String, Any?>, result: Result) {
        resp["code"] = CODE_SUCCEED
        resp["message"] = MESSAGE_SUCCEED
        resp["data"] = data
        result.success(resp)
    }

    /** 失败回调 */
    private fun failedCallback(resp: HashMap<String, Any>, code: Int, message: String?, result: Result) {
        resp["code"] = CODE_FAILED
        resp["message"] = MESSAGE_FAILED
        resp["data"] = hashMapOf(Pair("errorCode", code), Pair("errorMsg", message))
        result.success(resp)
    }
}
